/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_ODESOLID_H
#define M_ODESOLID_H

#include "solid.h"
#include "odeheaders.h"

namespace Engine
{
	/**
	*   A data structure used within ODESolids to store data describing  ODE geoms.
	*/
	class GeomData
	{
	public:
		GeomData()
		{
			geomID = 0;
			spaceID = 0;
			transformID = 0;
			trimeshDataID = 0;
		}

		dGeomID geomID;
		dSpaceID spaceID;
		dGeomID transformID;
		dTriMeshDataID trimeshDataID; // only used for Solids with trimeshes

		void SetShapeData(const ShapeData& shape)
		{
			m_shape = RefPtr<ShapeData>(shape.Clone());
		}

		ShapeData* GetShapeData()
		{
			return m_shape.GetPointer();
		}
		const ShapeData* GetShapeData()const
		{
			return m_shape.operator ->();
		}

	private:
		RefPtr<ShapeData> m_shape;
	};

	/**
	*   The ODE implementation of the Solid class.
	*/
	class OdeSolid: public Solid
	{
	public:
		typedef std::vector<GeomData> GeomVector;
		/**
		*   Constructor.
		*/
		OdeSolid(dWorldID worldID, dSpaceID spaceID, const std::string& name, SceneNodePtr node);

		/**
		*   Destructor
		*/
		virtual ~OdeSolid();

		/**
		*   Returns true if the Solid is enabled.
		*/
		virtual bool IsEnabled() const;

		/**
		*   Sets whether the Solid can collide with other Solids and be physically simulated.
		*   Forces applied to this Solid while disabled will be ignored.
		*/
		virtual void SetEnabled(bool e);

		/**
		*   Returns true if this is a static Solid.
		*/
		virtual bool IsStatic() const;

		/**
		*   Sets whether the Solid should be static or dynamic.
		*/
		virtual void SetStatic(bool s);

		/**
		*   Returns whether the Solid is affected by gravity.
		*/
		virtual bool IsGravityEnabled()const;

		/**
		*   Sets whether the Solid is affected by gravity.
		*/
		virtual void SetGravityEnabled(bool e);

		/**
		*   Removes the Solid from its current Space and adds it to the new Space.
		*/
		virtual void SetSpace(SpacePtr space);

		/**
		*   Set a new mass, center, and intertia for the solid.
		*/
		virtual void SetMass(const Mass& newmass);

		/**
		*   Returns the Solid's mass.  This will return 0 if the Solid is static.
		*/
		virtual REAL GetMass() const;

	private:

		/**
		*   Removes all forces and torques currently affecting this Solid.
		*/
		virtual void ZeroForcesImpl();

		/**
		*   Implementation of force applying.
		*/
		virtual void ApplyForce(const Force& f);

		/**
		*   Implementation of adding shape
		*/
		virtual void AddShapeImpl(const ShapeData& shape);

		/**
		*   Returns a constant reference to this Solid's transform from physic engine.
		*/
		virtual Matrix4<REAL> GetTransformImpl() const;

		/**
		*   Set Solid's transform to physic engine.
		*/
		virtual void SetTransformImpl(const Matrix4<REAL>& t);

		/**
		*   Destrory all associated geoms.
		*/
		void DestroyGeoms();

		/**
		*   Move all assocliated objects to space.
		*/
		void MoveToSpace();

		/**
		*   Add mass to body.
		*/
		void AddMass(dMass* newMass);

		/**
		*   Setup new added geometry.
		*/
		void SetupNewGeom(const GeomData& geom);

	private:
		dBodyID m_bodyID;
		dWorldID m_worldID;
		dSpaceID m_spaceID;

		GeomVector m_geoms; ///< list of geom data objects.

		bool m_enabled;
		bool m_static;
		bool m_gravityEnabled;

		Matrix4<REAL> m_transform;
	};
}

#endif
